﻿using System.Text;

namespace Base64Sample;

public static class Base64
{
    private static readonly string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    public static string Encode(byte[] bytes)
    {
        var sb = new StringBuilder();
        var mod = bytes.Length % 3;
        var padding = mod == 0 ? 0 : 3 - mod;
        for (var i = 0; i < bytes.Length + padding; i += 3)
        {
            var byte1 = bytes[i];
            var byte2 = i + 1 < bytes.Length ? bytes[i + 1] : 0;
            var byte3 = i + 2 < bytes.Length ? bytes[i + 2] : 0;
            sb.Append(Alphabet[byte1 >> 2]);
            sb.Append(Alphabet[(byte1 & 0x3) << 4 | byte2 >> 4]);
            sb.Append(i + 1 < bytes.Length ? Alphabet[(byte2 & 0xF) << 2 | byte3 >> 6] : '=');
            sb.Append(i + 2 < bytes.Length ? Alphabet[byte3 & 0x3F] : '=');
        }
        return sb.ToString();
    }

    public static byte[] Decode(string text)
    {
        var dict = Alphabet.ToDictionary(c => (int)c, Alphabet.IndexOf);
        using var stream = new MemoryStream();
        var bytes = Encoding.ASCII.GetBytes(text);
        for (var i = 0; i < bytes.Length; i += 4)
        {
            var (byte1, byte2, byte3, byte4) = (bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            stream.WriteByte((byte)(dict[byte1] << 2 | dict[byte2] >> 4));
            if (byte3 != '=')
            {
                stream.WriteByte((byte)(dict[byte2] << 4 | dict[byte3] >> 2));
            }
            if (byte4 != '=')
            {
                stream.WriteByte((byte)(dict[byte3] << 6 | dict[byte4]));
            }
        }
        return stream.ToArray();
    }
}
